package network.client;

import java.io.*;
import java.net.Socket;

import static network.client.Printer.print;

public class ClientApplication implements Closeable {
    private ClientStub stub = new ClientStub();
    private Thread sendWorker, receiveWorker;
    private final Object connectionMonitor = new Object();
    private String host;
    private int port;
     private boolean isConnected = false;
    private boolean isExit = false;

    public ClientStub getStub() {
        return stub;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public void start(String host, int port) {
        this.host = host;
        this.port = port;

        connect(getHost(), getPort());
        if (isConnected) {
            sendMessage();
            receiveMessage();
        }
    }

    private void connect(String host, int port) {
        try {
            getStub().connect(host, port);
            isConnected = true;
            print("Connection successful.");
        } catch (ChatException e) {
            print("Connection not established.");
        }
    }

    private void reconnect(String kbdInput) throws IOException {
        synchronized (connectionMonitor) {
            switch (kbdInput) {
                case "R":
                    try {
                        getStub().connect(getHost(), getPort());
                        isConnected = true;
                        print("Reconnect is success.Please, log in again.");
                        connectionMonitor.notify();
                    } catch (ChatException e) {
                        print("Connection not established.Reconnect or exit?(R/E)");
                        return;
                    }
                    break;
                case "E":
                    connectionMonitor.notify();
                    close();
                    break;
            }
        }
    }

    public void sendMessage() {
        sendWorker = new Thread(new Runnable() {
            @Override
            public void run() {
                try(BufferedReader kbd = new BufferedReader(new InputStreamReader(System.in))) {
                    while (true) {
                        String kbdInput = kbd.readLine().trim();

                        if(! isConnected ){
                            reconnect(kbdInput);
                            continue;
                        }

                        if (!stub.processClientRequest(kbdInput)) {
                            if (!"/exit".equals(kbdInput.trim().toLowerCase())) {
                                print("Incorrect command.");
                            } else {
                                isExit = true;
                                close();
                            }
                        }
                    }
                } catch (NullPointerException e) {
                    System.err.println("Error: Client application is closed.");
                } catch (IOException e) {
                    System.err.println("Problems: cannot read from keyboard.");
                } catch (ChatException e) {
                    print("Connection is closed.");
                }
            }
        });
        sendWorker.setDaemon(true);
        sendWorker.start();
    }

    public void receiveMessage() {
        receiveWorker = new Thread( new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        print(stub.processServerRequest());
                    } catch (ChatException e) {
                        if( isExit )
                            break;
                        synchronized (connectionMonitor) {
                            isConnected = false;
                            print("Connection is closed. Reconnect or exit?(R/E)");

                            try {
                                connectionMonitor.wait();
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }
                        }

                        if( !isConnected){
                            break;
                        }
                    }  catch (ClassNotFoundException e) {
                        print("Cannot read incoming message.");
                    }
                }
            }
        });
        receiveWorker.start();
    }

    @Override
    public void close() throws IOException {
        print("Client application is closed.");
        getStub().close();
    }

    public static void main(String[] args) {
        ClientApplication clientApplication = new ClientApplication();
        clientApplication.start("192.168.1.106", 888);

        if (args.length != 0) {
            try {
                Socket s = new Socket("localhost", Integer.parseInt(args[0]));
                System.setOut(new PrintStream(s.getOutputStream()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}